import { plainToInstance } from 'class-transformer';
import { validateOrReject, ValidationError } from 'class-validator';
import { Context, Middleware, Next } from 'koa';

import { IConstructor, RequestBodyType, ValidateRule } from '../types';
import { getValidateTarget, Typing } from '../utils.ts';

const commValidateFactory = (bodyType: RequestBodyType) => {
  return (Dto: IConstructor): Middleware => {
    return async (ctx: Context, next: Next) => {
      try {
        const validateTarget = getValidateTarget(ctx, bodyType);

        if (!validateTarget || Object.keys(validateTarget).length === 0) {
          ctx.throw(400, '请求参数为空');
        }

        const dto = plainToInstance(Dto, validateTarget);
        await validateOrReject(dto as any);
        await next();
      } catch (error) {
        let errMsg = '';
        if (Array.isArray(error)) {
          (error as ValidationError[]).forEach((item) => {
            if (!Typing.objIsEmpty(item.constraints)) {
              const keys = Object.keys(item.constraints as Record<string, any>);
              errMsg += (keys as string[]).reduce((pre, cur) => {
                return pre + item.constraints![cur] + ';';
              }, '');
            }
          });
        } else {
          errMsg += (error as Error).message;
        }
        ctx.throw(400, errMsg);
      }
    };
  };
};

export const validateBody = commValidateFactory('body');
export const validateQuery = commValidateFactory('query');
export const validateParams = commValidateFactory('params');

const customValidateFactory = (bodyType: RequestBodyType) => {
  return (rule: ValidateRule | string[]): Middleware => {
    return async (ctx: Context, next: Next) => {
      const validateTarget = getValidateTarget(ctx, bodyType);

      if (!validateTarget || Object.keys(validateTarget).length === 0) {
        ctx.throw(400, '请求参数为空');
      }

      let msg = '';
      if (Array.isArray(rule)) {
        rule.forEach((item) => {
          if (validateTarget[item]) {
            if (!String(validateTarget[item]).trim()) {
              msg += `${item}为空;`;
            }
          } else {
            msg += `${item}为必填项;`;
          }
        });
      } else {
        for (const propertyKey in rule) {
          if (!validateTarget[propertyKey]) {
            msg += `${propertyKey}为空`;
            break;
          } else {
            if (!rule[propertyKey].test(validateTarget[propertyKey])) {
              msg += `${propertyKey}格式错误`;
              break;
            }
          }
        }
      }

      if (msg) {
        ctx.throw(400, msg);
      } else {
        await next();
      }
    };
  };
};

export const regex4Body = customValidateFactory('body');
export const regex4Query = customValidateFactory('query');
export const regex4Params = customValidateFactory('params');
